﻿namespace FrameWork.Lib.HTML
{
    using System;
    using System.Collections.Specialized;
    using System.Text;

    internal class HtmlParser
    {
        private bool mRemoveEmptyElementText = false;
        private static char[] WHITESPACE_CHARS = " \t\r\n".ToCharArray();

        private static string DecodeScript(string script)
        {
            return script.Replace("[MIL-SCRIPT-LT]", "<").Replace("[MIL-SCRIPT-GT]", ">").Replace("[MIL-SCRIPT-CR]", "\r").Replace("[MIL-SCRIPT-LF]", "\n");
        }

        private static string EncodeScript(string script)
        {
            return script.Replace("<", "[MIL-SCRIPT-LT]").Replace(">", "[MIL-SCRIPT-GT]").Replace("\r", "[MIL-SCRIPT-CR]").Replace("\n", "[MIL-SCRIPT-LF]");
        }

        private int FindTagOpenNodeIndex(HtmlNodeCollection nodes, string name)
        {
            for (int i = nodes.Count - 1; i >= 0; i--)
            {
                if ((nodes[i] is HtmlElement) && !((!((HtmlElement) nodes[i]).Name.ToLower().Equals(name.ToLower()) || (((HtmlElement) nodes[i]).Nodes.Count != 0)) || ((HtmlElement) nodes[i]).IsTerminated))
                {
                    return i;
                }
            }
            return -1;
        }

        private StringCollection GetTokens(string input)
        {
            StringCollection strings = new StringCollection();
            int startIndex = 0;
            ParseStatus readText = ParseStatus.ReadText;
            while (startIndex < input.Length)
            {
                int num3;
                switch (readText)
                {
                    case ParseStatus.ReadText:
                        if (((startIndex + 2) < input.Length) && input.Substring(startIndex, 2).Equals("</"))
                        {
                            startIndex += 2;
                            strings.Add("</");
                            readText = ParseStatus.ReadEndTag;
                        }
                        else if (input.Substring(startIndex, 1).Equals("<"))
                        {
                            startIndex++;
                            strings.Add("<");
                            readText = ParseStatus.ReadStartTag;
                        }
                        else
                        {
                            int index = input.IndexOf("<", startIndex);
                            if (index == -1)
                            {
                                strings.Add(input.Substring(startIndex));
                                return strings;
                            }
                            strings.Add(input.Substring(startIndex, index - startIndex));
                            startIndex = index;
                        }
                        break;

                    case ParseStatus.ReadStartTag:
                        while ((startIndex < input.Length) && (input.Substring(startIndex, 1).IndexOfAny(WHITESPACE_CHARS) != -1))
                        {
                            startIndex++;
                        }
                        num3 = startIndex;
                        while ((startIndex < input.Length) && (input.Substring(startIndex, 1).IndexOfAny(" \r\n\t/>".ToCharArray()) == -1))
                        {
                            startIndex++;
                        }
                        strings.Add(input.Substring(num3, startIndex - num3));
                        while ((startIndex < input.Length) && (input.Substring(startIndex, 1).IndexOfAny(WHITESPACE_CHARS) != -1))
                        {
                            startIndex++;
                        }
                        if (((startIndex + 1) < input.Length) && input.Substring(startIndex, 1).Equals("/>"))
                        {
                            strings.Add("/>");
                            readText = ParseStatus.ReadText;
                            startIndex += 2;
                        }
                        else if ((startIndex < input.Length) && input.Substring(startIndex, 1).Equals(">"))
                        {
                            strings.Add(">");
                            readText = ParseStatus.ReadText;
                            startIndex++;
                        }
                        else
                        {
                            readText = ParseStatus.ReadAttributeName;
                        }
                        break;

                    case ParseStatus.ReadEndTag:
                        while ((startIndex < input.Length) && (input.Substring(startIndex, 1).IndexOfAny(WHITESPACE_CHARS) != -1))
                        {
                            startIndex++;
                        }
                        num3 = startIndex;
                        while ((startIndex < input.Length) && (input.Substring(startIndex, 1).IndexOfAny(" \r\n\t>".ToCharArray()) == -1))
                        {
                            startIndex++;
                        }
                        strings.Add(input.Substring(num3, startIndex - num3));
                        while ((startIndex < input.Length) && (input.Substring(startIndex, 1).IndexOfAny(WHITESPACE_CHARS) != -1))
                        {
                            startIndex++;
                        }
                        if ((startIndex < input.Length) && input.Substring(startIndex, 1).Equals(">"))
                        {
                            strings.Add(">");
                            readText = ParseStatus.ReadText;
                            startIndex++;
                        }
                        break;

                    case ParseStatus.ReadAttributeName:
                    {
                        while ((startIndex < input.Length) && (input.Substring(startIndex, 1).IndexOfAny(WHITESPACE_CHARS) != -1))
                        {
                            startIndex++;
                        }
                        int num4 = startIndex;
                        while ((startIndex < input.Length) && (input.Substring(startIndex, 1).IndexOfAny(" \r\n\t/>=".ToCharArray()) == -1))
                        {
                            startIndex++;
                        }
                        strings.Add(input.Substring(num4, startIndex - num4));
                        while ((startIndex < input.Length) && (input.Substring(startIndex, 1).IndexOfAny(WHITESPACE_CHARS) != -1))
                        {
                            startIndex++;
                        }
                        if (((startIndex + 1) < input.Length) && input.Substring(startIndex, 2).Equals("/>"))
                        {
                            strings.Add("/>");
                            readText = ParseStatus.ReadText;
                            startIndex += 2;
                        }
                        else if ((startIndex < input.Length) && input.Substring(startIndex, 1).Equals(">"))
                        {
                            strings.Add(">");
                            readText = ParseStatus.ReadText;
                            startIndex++;
                        }
                        else if ((startIndex < input.Length) && input.Substring(startIndex, 1).Equals("="))
                        {
                            strings.Add("=");
                            startIndex++;
                            readText = ParseStatus.ReadAttributeValue;
                        }
                        else if ((startIndex < input.Length) && input.Substring(startIndex, 1).Equals("/"))
                        {
                            startIndex++;
                        }
                        break;
                    }
                    default:
                        if (readText == ParseStatus.ReadAttributeValue)
                        {
                            int num5;
                            while ((startIndex < input.Length) && (input.Substring(startIndex, 1).IndexOfAny(WHITESPACE_CHARS) != -1))
                            {
                                startIndex++;
                            }
                            if ((startIndex < input.Length) && input.Substring(startIndex, 1).Equals("\""))
                            {
                                num5 = startIndex;
                                startIndex++;
                                while ((startIndex < input.Length) && !input.Substring(startIndex, 1).Equals("\""))
                                {
                                    startIndex++;
                                }
                                if ((startIndex < input.Length) && input.Substring(startIndex, 1).Equals("\""))
                                {
                                    startIndex++;
                                }
                                strings.Add(input.Substring(num5 + 1, (startIndex - num5) - 2));
                                readText = ParseStatus.ReadAttributeName;
                            }
                            else if ((startIndex < input.Length) && input.Substring(startIndex, 1).Equals("'"))
                            {
                                num5 = startIndex;
                                startIndex++;
                                while ((startIndex < input.Length) && !input.Substring(startIndex, 1).Equals("'"))
                                {
                                    startIndex++;
                                }
                                if ((startIndex < input.Length) && input.Substring(startIndex, 1).Equals("'"))
                                {
                                    startIndex++;
                                }
                                strings.Add(input.Substring(num5 + 1, (startIndex - num5) - 2));
                                readText = ParseStatus.ReadAttributeName;
                            }
                            else
                            {
                                num5 = startIndex;
                                while ((startIndex < input.Length) && (input.Substring(startIndex, 1).IndexOfAny(" \r\n\t/>".ToCharArray()) == -1))
                                {
                                    startIndex++;
                                }
                                strings.Add(input.Substring(num5, startIndex - num5));
                                while ((startIndex < input.Length) && (input.Substring(startIndex, 1).IndexOfAny(WHITESPACE_CHARS) != -1))
                                {
                                    startIndex++;
                                }
                                readText = ParseStatus.ReadAttributeName;
                            }
                            if (((startIndex + 1) < input.Length) && input.Substring(startIndex, 2).Equals("/>"))
                            {
                                strings.Add("/>");
                                readText = ParseStatus.ReadText;
                                startIndex += 2;
                            }
                            else if ((startIndex < input.Length) && input.Substring(startIndex, 1).Equals(">"))
                            {
                                strings.Add(">");
                                startIndex++;
                                readText = ParseStatus.ReadText;
                            }
                        }
                        break;
                }
            }
            return strings;
        }

        private void MoveNodesDown(ref HtmlNodeCollection nodes, int node_index, HtmlElement new_parent)
        {
            int num;
            for (num = node_index; num < nodes.Count; num++)
            {
                new_parent.Nodes.Add(nodes[num]);
                nodes[num].SetParent(new_parent);
            }
            int count = nodes.Count;
            for (num = node_index; num < count; num++)
            {
                nodes.RemoveAt(node_index);
            }
            new_parent.IsExplicitlyTerminated = true;
        }

        public HtmlNodeCollection Parse(string html)
        {
            HtmlNodeCollection nodes = new HtmlNodeCollection(null);
            html = this.PreprocessScript(html, "script");
            html = this.PreprocessScript(html, "style");
            html = this.RemoveComments(html);
            html = this.RemoveSGMLComments(html);
            StringCollection tokens = this.GetTokens(html);
            int num = 0;
            HtmlElement node = null;
            while (num < tokens.Count)
            {
                string str;
                if ("<".Equals(tokens[num]))
                {
                    num++;
                    if (num >= tokens.Count)
                    {
                        return nodes;
                    }
                    str = tokens[num];
                    num++;
                    node = new HtmlElement(str);
                    while (((num < tokens.Count) && !">".Equals(tokens[num])) && !"/>".Equals(tokens[num]))
                    {
                        HtmlAttribute attribute;
                        string name = tokens[num];
                        num++;
                        if ((num < tokens.Count) && "=".Equals(tokens[num]))
                        {
                            string str3;
                            num++;
                            if (num < tokens.Count)
                            {
                                str3 = tokens[num];
                            }
                            else
                            {
                                str3 = null;
                            }
                            num++;
                            attribute = new HtmlAttribute(name, HtmlEncoder.DecodeValue(str3));
                            node.Attributes.Add(attribute);
                        }
                        else if (num < tokens.Count)
                        {
                            attribute = new HtmlAttribute(name, null);
                            node.Attributes.Add(attribute);
                        }
                    }
                    nodes.Add(node);
                    if ((num < tokens.Count) && "/>".Equals(tokens[num]))
                    {
                        node.IsTerminated = true;
                        num++;
                        node = null;
                    }
                    else if ((num < tokens.Count) && ">".Equals(tokens[num]))
                    {
                        num++;
                    }
                }
                else if (">".Equals(tokens[num]))
                {
                    num++;
                }
                else if ("</".Equals(tokens[num]))
                {
                    num++;
                    if (num >= tokens.Count)
                    {
                        return nodes;
                    }
                    str = tokens[num];
                    num++;
                    int num2 = this.FindTagOpenNodeIndex(nodes, str);
                    if (num2 != -1)
                    {
                        this.MoveNodesDown(ref nodes, num2 + 1, (HtmlElement) nodes[num2]);
                    }
                    while ((num < tokens.Count) && !">".Equals(tokens[num]))
                    {
                        num++;
                    }
                    if ((num < tokens.Count) && ">".Equals(tokens[num]))
                    {
                        num++;
                    }
                    node = null;
                }
                else
                {
                    string input = tokens[num];
                    if (this.mRemoveEmptyElementText)
                    {
                        input = this.RemoveWhitespace(input);
                    }
                    input = DecodeScript(input);
                    if (!this.mRemoveEmptyElementText || (input.Length != 0))
                    {
                        if (!((node != null) && node.NoEscaping))
                        {
                            input = HtmlEncoder.DecodeValue(input);
                        }
                        HtmlText text = new HtmlText(input);
                        nodes.Add(text);
                    }
                    num++;
                }
            }
            return nodes;
        }

        private string PreprocessScript(string input, string tag_name)
        {
            StringBuilder builder = new StringBuilder();
            int startIndex = 0;
            int length = tag_name.Length;
            while (startIndex < input.Length)
            {
                bool flag = false;
                if ((((startIndex + length) + 1) < input.Length) && input.Substring(startIndex, length + 1).ToLower().Equals("<" + tag_name))
                {
                    while (true)
                    {
                        if (startIndex >= input.Length)
                        {
                            break;
                        }
                        if (input.Substring(startIndex, 1).Equals(">"))
                        {
                            builder.Append(">");
                            startIndex++;
                            break;
                        }
                        if (((startIndex + 1) < input.Length) && input.Substring(startIndex, 2).Equals("/>"))
                        {
                            builder.Append("/>");
                            startIndex += 2;
                            flag = true;
                            break;
                        }
                        if (input.Substring(startIndex, 1).Equals("\""))
                        {
                            builder.Append("\"");
                            startIndex++;
                            while ((startIndex < input.Length) && !input.Substring(startIndex, 1).Equals("\""))
                            {
                                builder.Append(input.Substring(startIndex, 1));
                                startIndex++;
                            }
                            if (startIndex < input.Length)
                            {
                                startIndex++;
                                builder.Append("\"");
                            }
                        }
                        else if (input.Substring(startIndex, 1).Equals("'"))
                        {
                            builder.Append("'");
                            startIndex++;
                            while ((startIndex < input.Length) && !input.Substring(startIndex, 1).Equals("'"))
                            {
                                builder.Append(input.Substring(startIndex, 1));
                                startIndex++;
                            }
                            if (startIndex < input.Length)
                            {
                                startIndex++;
                                builder.Append("'");
                            }
                        }
                        else
                        {
                            builder.Append(input.Substring(startIndex, 1));
                            startIndex++;
                        }
                    }
                    if (startIndex >= input.Length)
                    {
                        break;
                    }
                    if (!flag)
                    {
                        StringBuilder builder2 = new StringBuilder();
                        while ((((startIndex + length) + 3) < input.Length) && !input.Substring(startIndex, length + 3).ToLower().Equals("</" + tag_name + ">"))
                        {
                            builder2.Append(input.Substring(startIndex, 1));
                            startIndex++;
                        }
                        builder.Append(EncodeScript(builder2.ToString()));
                        builder.Append("</" + tag_name + ">");
                        if (((startIndex + length) + 3) < input.Length)
                        {
                            startIndex += length + 3;
                        }
                    }
                }
                else
                {
                    builder.Append(input.Substring(startIndex, 1));
                    startIndex++;
                }
            }
            return builder.ToString();
        }

        private string RemoveComments(string input)
        {
            StringBuilder builder = new StringBuilder();
            int startIndex = 0;
            bool flag = false;
            while (startIndex < input.Length)
            {
                if (((startIndex + 4) < input.Length) && input.Substring(startIndex, 4).Equals("<!--"))
                {
                    startIndex += 4;
                    startIndex = input.IndexOf("-->", startIndex);
                    if (startIndex == -1)
                    {
                        break;
                    }
                    startIndex += 3;
                }
                else if (input.Substring(startIndex, 1).Equals("<"))
                {
                    flag = true;
                    builder.Append("<");
                    startIndex++;
                }
                else if (input.Substring(startIndex, 1).Equals(">"))
                {
                    flag = false;
                    builder.Append(">");
                    startIndex++;
                }
                else
                {
                    int num2;
                    if (input.Substring(startIndex, 1).Equals("\"") && flag)
                    {
                        num2 = startIndex;
                        startIndex++;
                        startIndex = input.IndexOf("\"", startIndex);
                        if (startIndex == -1)
                        {
                            break;
                        }
                        startIndex++;
                        builder.Append(input.Substring(num2, startIndex - num2));
                    }
                    else if (input.Substring(startIndex, 1).Equals("'") && flag)
                    {
                        num2 = startIndex;
                        startIndex++;
                        startIndex = input.IndexOf("'", startIndex);
                        if (startIndex == -1)
                        {
                            break;
                        }
                        startIndex++;
                        builder.Append(input.Substring(num2, startIndex - num2));
                    }
                    else
                    {
                        builder.Append(input.Substring(startIndex, 1));
                        startIndex++;
                    }
                }
            }
            return builder.ToString();
        }

        private string RemoveSGMLComments(string input)
        {
            StringBuilder builder = new StringBuilder();
            int startIndex = 0;
            bool flag = false;
            while (startIndex < input.Length)
            {
                if (((startIndex + 2) < input.Length) && input.Substring(startIndex, 2).Equals("<!"))
                {
                    startIndex += 2;
                    startIndex = input.IndexOf(">", startIndex);
                    if (startIndex == -1)
                    {
                        break;
                    }
                    startIndex += 3;
                }
                else if (input.Substring(startIndex, 1).Equals("<"))
                {
                    flag = true;
                    builder.Append("<");
                    startIndex++;
                }
                else if (input.Substring(startIndex, 1).Equals(">"))
                {
                    flag = false;
                    builder.Append(">");
                    startIndex++;
                }
                else
                {
                    int num2;
                    if (input.Substring(startIndex, 1).Equals("\"") && flag)
                    {
                        num2 = startIndex;
                        startIndex++;
                        startIndex = input.IndexOf("\"", startIndex);
                        if (startIndex == -1)
                        {
                            break;
                        }
                        startIndex++;
                        builder.Append(input.Substring(num2, startIndex - num2));
                    }
                    else if (input.Substring(startIndex, 1).Equals("'") && flag)
                    {
                        num2 = startIndex;
                        startIndex++;
                        startIndex = input.IndexOf("'", startIndex);
                        if (startIndex == -1)
                        {
                            break;
                        }
                        startIndex++;
                        builder.Append(input.Substring(num2, startIndex - num2));
                    }
                    else
                    {
                        builder.Append(input.Substring(startIndex, 1));
                        startIndex++;
                    }
                }
            }
            return builder.ToString();
        }

        private string RemoveWhitespace(string input)
        {
            return input.Replace("\r", "").Replace("\n", "").Replace("\t", " ").Trim();
        }

        public bool RemoveEmptyElementText
        {
            get
            {
                return this.mRemoveEmptyElementText;
            }
            set
            {
                this.mRemoveEmptyElementText = value;
            }
        }

        private enum ParseStatus
        {
            ReadText,
            ReadEndTag,
            ReadStartTag,
            ReadAttributeName,
            ReadAttributeValue
        }
    }
}

